/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://www.opensourcephysics.org/>
 */

package org.opensourcephysics.tools;

import java.util.*;

import javax.swing.BorderFactory;

import org.opensourcephysics.controls.XMLControl;
import org.opensourcephysics.display.DatasetManager;

/**
 * A FunctionEditor for Parameters.
 *
 * @author Douglas Brown
 */
public class ParamEditor extends FunctionEditor {

	protected double[] paramValues = new double[0];
	private DatasetManager data;
	private FunctionEditor[] functionEditors;

	/**
	 * Default constructor
	 */
  public ParamEditor() {
  	super();
  	paramEditor = this;
  }

	/**
	 * Constructor using a DatasetManager to define initial parameters
	 * 
	 * @param input the DatasetManager
	 */
  public ParamEditor(DatasetManager input) {
  	this();
  	data = input;
  	refreshParametersFromData();
  }

	/**
	 * Gets an array containing copies of the current parameters.
	 * 
	 * @return an array of Parameters
	 */
	public Parameter[] getParameters() {
		Parameter[] params = new Parameter[objects.size()];
		for (int i = 0; i < objects.size(); i++) {
			Parameter next = (Parameter)objects.get(i);
			params[i] = new Parameter(next.paramName, next.expression);
			params[i].setExpressionEditable(next.isExpressionEditable());
			params[i].setNameEditable(next.isNameEditable());
			params[i].setDescription(next.getDescription());
			params[i].value = next.value;
		}
		return params;
	}

	/**
	 * Replaces the current parameters with new ones.
	 * 
	 * @param params an array of Parameters
	 */
	public void setParameters(Parameter[] params) {
		List list = new ArrayList();
		for (int i = 0; i < params.length; i++) {
			list.add(params[i]);
		}
		setObjects(list);
	}
	
	/**
	 * Sets the function editors that use these parameters.
	 * 
	 * @param editors an array of FunctionEditors
	 */
	public void setFunctionEditors(FunctionEditor[] editors) {
		functionEditors = editors;
	}
	
	/**
	 * Gets the current parameter values.
	 * 
	 * @return an array of values
	 */
	public double[] getValues() {
		return paramValues;
	}

  /**
   * Returns the name of the object.
   *
   * @param obj the object
   * @return the name
   */
  public String getName(Object obj) {
    return obj == null? null: ((Parameter)obj).paramName;
  }

  /**
   * Returns the expression of the object.
   *
   * @param obj the object
   * @return the expression
   */
  public String getExpression(Object obj) {
    return obj == null? null: ((Parameter)obj).expression;
  }

  /**
   * Returns a tooltip for the object.
   *
   * @param obj the object
   * @return the tooltip
   */
  public String getTooltip(Object obj) {
  	String s = ((Parameter)obj).getDescription();
  	if (s == null)
  		s = ToolsRes.getString("ParamEditor.Table.Cell.Name.Tooltip"); //$NON-NLS-1$
    return s;
  }

	/**
	 * Determines if an object's name is editable.
	 * 
	 * @param obj the object
	 * @return true if the name is editable
	 */
	public boolean isNameEditable(Object obj) {
		return ((Parameter)obj).isNameEditable();
	}
	
	/**
	 * Determines if an object's expression is editable.
	 * 
	 * @param obj the object
	 * @return true if the expression is editable
	 */
	public boolean isExpressionEditable(Object obj) {
		return ((Parameter)obj).isExpressionEditable();
	}
	
	/**
	 * Evaluates an object.
	 */
	protected void evaluateObject(Object obj) {
		Parameter p = (Parameter)obj;
		p.evaluate(objects);
	}
	
	/**
	 * Evaluates parameters that depend on the named parameter.
	 * 
	 * @param paramName the name
	 * @param paramValue the parameter value
	 * @return a list of evaluated parameters
	 */
	public ArrayList evaluateDependents(Parameter seed) {
		ArrayList temp = new ArrayList();
		ArrayList toRemove = new ArrayList();
		for (int i = 0; i < evaluate.size(); i++) {
			Parameter param = (Parameter)evaluate.get(i);
    	if (param.paramName.equals(seed.paramName)) {
  			temp.add(seed);
  			toRemove.add(seed);
    		for (int j = i+1; j < evaluate.size(); j++) {
    			Parameter p = (Parameter)evaluate.get(j);
    			temp.add(new Parameter(p.paramName, p.expression));
    		}
    		// evaluate temp list
    		for (int j = 0; j < temp.size(); j++) {
    			// for each parameter, evaluate and set paramValues element
    			Parameter p = (Parameter)temp.get(j);
    			p.evaluate(temp);
    			if (getReferences(p.getName(), null).isEmpty()) toRemove.add(p);
    		}
    		temp.removeAll(toRemove);
    		return temp;
    	}
		}
		return temp;
	}

	/**
	 * Evaluates all current objects. 
	 */
	public void evaluateAll() {
		super.evaluateAll();
		if (this.getClass() != ParamEditor.class) return;
		if (paramValues.length != objects.size()) {
			paramValues = new double[objects.size()];
		}
		for (int i = 0; i < evaluate.size(); i++) {
			Parameter p = (Parameter)evaluate.get(i);
			p.evaluate(objects);
		}
		for (int i = 0; i < objects.size(); i++) {
			Parameter p = (Parameter)objects.get(i);
			paramValues[i] = p.getValue();
		}
	}

  /**
   * Returns true if a name is already in use.
   *
   * @param obj the object (may be null) 
   * @param name the proposed name for the object
   * @return true if duplicate
   */
  protected boolean isDisallowedName(Object obj, String name) {
		boolean disallowed = super.isDisallowedName(obj, name);
		if (functionEditors != null) {
			for (int i = 0; i < functionEditors.length; i++) {
				disallowed = disallowed || functionEditors[i].isDisallowedName(null, name);
			}
		}
		return disallowed;
  }

  /**
   * Pastes the clipboard contents.
   */
  protected void paste() {
		XMLControl[] controls = getClipboardContents();
		if (controls == null) return;
		for (int i = 0; i < controls.length; i++) {
			// create a new object
			Parameter param = (Parameter)controls[i].loadObject(null);
			param.setNameEditable(true);
			param.setExpressionEditable(true);
			addObject(param, true);
		}
		evaluateAll();
  }
  
  /**
   * Returns true if the object expression is invalid.
   */
  protected boolean isInvalidExpression(Object obj) {
		return Double.isNaN(((Parameter)obj).getValue());
  }
  
  /**
   * Creates an object with specified name and expression.
   * This always returns a new Parameter but copies the editable properties.
   *
   * @param name the name
   * @param expression the expression
   * @param obj ignored
   * @return the object
   */
  protected Object createObject(String name, String expression, Object obj) {
		Parameter original = (Parameter)obj;
  	if (original != null
  			&& original.paramName.equals(name) 
  			&& original.expression.equals(expression))
  		return original;
  	Parameter p = new Parameter(name, expression);
  	if (original != null) {
    	p.setExpressionEditable(original.isExpressionEditable());
    	p.setNameEditable(original.isNameEditable());
  		p.setDescription(original.getDescription());
  	}
    return p;
  }

  /**
   * Refreshes the GUI.
   */
  protected void refreshGUI() {
  	super.refreshGUI();
		newButton.setToolTipText(ToolsRes.getString("ParamEditor.Button.New.Tooltip")); //$NON-NLS-1$
		setBorder(BorderFactory.createTitledBorder(ToolsRes.getString("ParamEditor.Border.Title"))); //$NON-NLS-1$
  }
  
  /**
   * Refreshes the parameters based on the current data properties map.
   */
  protected void refreshParametersFromData() {
  	if (data == null) return;
  	ArrayList temp = new ArrayList();
  	Map map = data.getProperties();
  	Iterator it = map.keySet().iterator();
  	while (it.hasNext()) {
  		temp.add(it.next());
  	}
  	it = temp.iterator();
  	// identify values that have changed
  	while (it.hasNext()) {
  		String name = it.next().toString();
  		String val = map.get(name).toString();
  		Parameter p = (Parameter)getObject(name);
  		if (p == null) {
        p = new Parameter(name, val); 
        p.setNameEditable(false);
        p.setExpressionEditable(false);
        addObject(p, false);  			
  		}
	  	// change only uneditable parameter values
  		else if (p.getName().equals(name)
						&& !p.isExpressionEditable()) {
  			setExpression(name, val, false);
  		}
  	}
  }
  
  /**
   * Refreshes the parameters based on the current data properties map.
   */
  protected void refreshParametersFromFunction(UserFunction f) {
  	// identify values that have changed
  	for (int i = 0; i < f.getParameterCount();i++) {
  		String name = f.getParameterName(i);
   		String val = String.valueOf(f.getParameterValue(i));
  		Parameter p = (Parameter)getObject(name);
  		if (p == null) {
        p = new Parameter(name, val); 
        p.setNameEditable(false);
        p.setExpressionEditable(false);
        addObject(p, false);  			
  		}
	  	// change parameter value
  		else {
  			setExpression(name, val, false);
  		}
  	}
  }
  
  /**
   * Returns the default name for newly created objects.
   */
  protected String getDefaultName() {
  	return ToolsRes.getString("ParamEditor.New.Name.Default"); //$NON-NLS-1$
  }
  
}
	

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2007  The Open Source Physics project
 *                     http://www.opensourcephysics.org
 */
